package database;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.hibernate.FetchMode;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HibernateProductsDAO implements IProductsDAO
{
    final static Logger logger = LoggerFactory.getLogger(HibernateProductsDAO.class);
    final static String NEW_LINE = System.getProperty("line.separator");
    static private HibernateProductsDAO instance = null;
    
    static HibernateProductsDAO getInstance()
    {
        if (instance == null)
        {
            instance = new HibernateProductsDAO();
        }
        return instance;
    }
    
    private static final SessionFactory sessionFactory;
    static
    {
        try
        {
            // Create the SessionFactory from hibernate.cfg.xml
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
        }
        catch (Throwable ex)
        {
            // Make sure you log the exception, as it might be swallowed
            logger.info("Initial SessionFactory Creation Failed");
            throw new ExceptionInInitializerError(ex);
        }
    }
    
    public static SessionFactory getSessionFactory()
    {
        return sessionFactory;
    }
    
    private HibernateProductsDAO()
    {}
    
    @Override
    public synchronized Product AddProduct(String productName, String productUnit, Double productQuantityUnit, String productImage)
    {
        Product product = new Product(productName, productUnit, productQuantityUnit, productImage);
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Begin Add/Update Product");
            System.out.println("Begin Add/Update Product");
            transaction = session.beginTransaction();
            session.saveOrUpdate(product);
            transaction.commit();
            logger.info("Add/Update Product Done");
            System.out.println("Add/Update Product Done");
            logger.info("Product Details:" + NEW_LINE +  product.toString());
            System.out.println("Product Details:" + NEW_LINE +  product.toString());
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
        return product;
    }
    
    @Override
    public synchronized Product AddProduct(Product product)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Begin Add/Update Product");
            System.out.println("Begin Add/Update Product");
            transaction = session.beginTransaction();
            session.saveOrUpdate(product);
            transaction.commit();
            logger.info("Add/Update Product Done");
            System.out.println("Add/Update Product Done");
            logger.info("Product Details:" + NEW_LINE +  product.toString());
            System.out.println("Product Details:" + NEW_LINE +  product.toString());
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
        return product;
    }
    
    @Override
    public synchronized Supermarket AddSupermarket(String supermarketName, String supermarketBranch, Double supermarketLocationLatitude, Double supermarketLocationLongitude)
    {
        Supermarket supermarket = new Supermarket(supermarketName, supermarketBranch, supermarketLocationLatitude, supermarketLocationLongitude);
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Begin Add/Update Supermarket");
            System.out.println("Begin Add/Update Supermarket");
            transaction = session.beginTransaction();
            session.saveOrUpdate(supermarket);
            transaction.commit();
            logger.info("Add/Update Supermarket Done");
            System.out.println("Add/Update Supermarket Done");
            logger.info("Supermarket Details:" + NEW_LINE +  supermarket.toString());
            System.out.println("Supermarket Details:" + NEW_LINE +  supermarket.toString());
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
        return supermarket;
    }
    
    @Override
    public synchronized Supermarket AddSupermarket(Supermarket supermarket)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Begin Add/Update Supermarket");
            System.out.println("Begin Add/Update Supermarket");
            transaction = session.beginTransaction();
            session.saveOrUpdate(supermarket);
            transaction.commit();
            logger.info("Add/Update Supermarket Done");
            System.out.println("Add/Update Supermarket Done");
            logger.info("Supermarket Details:" + NEW_LINE +  supermarket.toString());
            System.out.println("Supermarket Details:" + NEW_LINE +  supermarket.toString());
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
        return supermarket;
    }
    
    @Override
    public synchronized ProductSupermarket AddProductToSupermarket(Supermarket supermarket, Product product, Double price)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        ProductSupermarket productSupermarket = new ProductSupermarket();
        try
        {
            logger.info("Begin Add/Update Product To Supermarket");
            System.out.println("Begin Add/Update Product To Supermarket");
            transaction = session.beginTransaction();
            productSupermarket.setProduct(product);
            productSupermarket.setSupermarket(supermarket);
            productSupermarket.setPrice(price);
            product.getProductSupermarkets().add(productSupermarket);
            session.saveOrUpdate(productSupermarket);
            transaction.commit();
            logger.info("Add/Update Product To Supermarket Done");
            System.out.println("Add/Update Product To Supermarket Done");
            logger.info("Product Supermarket Details:" + NEW_LINE +  productSupermarket.toString());
            System.out.println("Product Supermarket Details:" + NEW_LINE +  productSupermarket.toString());
            
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
        return productSupermarket;
    }

    @Override
    public synchronized Product FindProductById(Integer productId)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Get Product By Id");
            System.out.println("Get Product By Id");
            transaction = session.beginTransaction();
            Product product = (Product) session.createCriteria(Product.class)
                            .setFetchMode("productSupermarkets", FetchMode.JOIN)
                            .add(Restrictions.eq("productId", productId))
                            .uniqueResult();
            transaction.commit();
            logger.info("Product Retrieved");
            logger.info("Product Retrieved");
            System.out.println("Product Retrieved");
            return product;
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
    }
    
    @Override
    public synchronized Product FindProduct(String productName, String productUnit, Double productQuantityPerUnit)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Get Product By Name");
            System.out.println("Get Product By Name");
            transaction = session.beginTransaction();
            Product product = (Product) session.createCriteria(Product.class)
                            .setFetchMode("productSupermarkets", FetchMode.JOIN)
                            .add(Restrictions.eq("productName", productName))
                            .add(Restrictions.eq("productUnit", productUnit))
                            .add(Restrictions.eq("productQuantityPerUnit", productQuantityPerUnit))
                            .uniqueResult();
            transaction.commit();
            logger.info("Product Retrieved");
            System.out.println("Product Retrieved");
            return product;
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
    }
    
    @Override
    public synchronized List<Product> FindProductsByName(String productName)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Get Product By Name");
            System.out.println("Get Product By Name");
            transaction = session.beginTransaction();
            List<Product> products = session.createCriteria(Product.class)
                                     .setFetchMode("productSupermarkets", FetchMode.JOIN)
                                     .add(Restrictions.eq("productName", productName))
                                     .list();
            transaction.commit();
            logger.info("Product Retrieved");
            System.out.println("Product Retrieved");
            return products;
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
    }
    
    @Override
    public synchronized Supermarket FindSupermarketById(Integer supermarketId)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Get Supermarket By Id");
            System.out.println("Get Supermarket By Id");
            transaction = session.beginTransaction();
            Supermarket supermarket = (Supermarket) session.createCriteria(Supermarket.class)
                                    .setFetchMode("productSupermarkets", FetchMode.JOIN)
                                    .add(Restrictions.eq("supermarketId", supermarketId))
                                    .uniqueResult();
            transaction.commit();
            logger.info("Supermarket Retrieved");
            System.out.println("Supermarket Retrieved");
            return supermarket;
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
    }
    
    @Override
    public synchronized Supermarket FindSupermarket(String supermarketName, String supermarketBranch, Double supermarketLocationLatitude, Double supermarketLocationLongitude)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Get Supermarket By Name");
            System.out.println("Get Supermarket By Name");
            transaction = session.beginTransaction();
            Supermarket supermarket = (Supermarket) session.createCriteria(Supermarket.class)
                                      .setFetchMode("productSupermarkets", FetchMode.JOIN)
                                      .add(Restrictions.eq("supermarketName", supermarketName))
                                      .add(Restrictions.eq("supermarketBranch", supermarketBranch))
                                      .add(Restrictions.eq("supermarketLocationLatitude", supermarketLocationLatitude))
                                      .add(Restrictions.eq("supermarketLocationLongitude", supermarketLocationLongitude))
                                      .uniqueResult();
            transaction.commit();
            logger.info("Supermarket Retrieved");
            System.out.println("Supermarket Retrieved");
            return supermarket;
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
    }
    
       @Override
       public synchronized ProductSupermarket FindProductSupermarket(Product product, Supermarket supermarket)
        {
            Session session = HibernateProductsDAO.getSessionFactory().openSession();
            Transaction transaction = null;
            try
            {
                logger.info("Get Product, Supermarket and Price");
                System.out.println("Get Product, Supermarket and Price");
                transaction = session.beginTransaction();
                List<ProductSupermarket> productSupermarkets = session.createCriteria(ProductSupermarket.class)
                                                               .list();
                transaction.commit();
                for (Iterator<ProductSupermarket> iterPS = productSupermarkets.iterator(); iterPS.hasNext();)
                {
                    ProductSupermarket productSupermarket = (ProductSupermarket) iterPS.next();
                    if (productSupermarket.getProduct().getProductId().equals(product.getProductId()) &&
                        productSupermarket.getSupermarket().getSupermarketId().equals(supermarket.getSupermarketId()))
                    {
                        logger.info("Supermarket Retrieved");
                        System.out.println("Supermarket Retrieved");
                        return productSupermarket;
                    }
                } 
                return null;
            }
            catch (HibernateException e)
            {
                if (transaction != null && transaction.isActive())
                {
                    try
                    {
                        // Second try catch as the rollback could fail as well
                        transaction.rollback();
                    }
                    catch (HibernateException e1)
                    {
                        logger.info("Error Rolling Back Transaction");
                    }
                }
                // throw again the first exception
                 throw e;
            }
            finally
            {
                session.close();
            }
        }
       
    @Override
    public synchronized List<Supermarket> FindSupermarketsByName(String supermarketName)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Get Supermarket By Name");
            System.out.println("Get Supermarket By Name");
            transaction = session.beginTransaction();
            List<Supermarket> supermarkets = session.createCriteria(Supermarket.class)
                                             .setFetchMode("productSupermarkets", FetchMode.JOIN)
                                             .add(Restrictions.eq("supermarketName", supermarketName))
                                             .list();
            transaction.commit();
            logger.info("Supermarket Retrieved");
            System.out.println("Supermarket Retrieved");
            return supermarkets;
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
    }
    
    @Override
    public synchronized List<Product> FindAllProducts()
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Get Products");
            System.out.println("Get Products");
            transaction = session.beginTransaction();
            Query query = session.createQuery("from Product");
            List<Product> products = query.list();
            transaction.commit();
            return products;
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
    }

    @Override
    public synchronized List<Supermarket> FindAllSupermarkets()
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Get Supermarkets");
            transaction = session.beginTransaction();
            Query query = session.createQuery("from Supermarket");
            List<Supermarket> supermarkets = query.list();
            transaction.commit();
            return supermarkets;
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
    }
    
    @Override
    public synchronized Set<ProductSupermarket> FindAllProductSupermarkets(Product product)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Get ProductSupermarkets");
            transaction = session.beginTransaction();
            product = (Product) session.createCriteria(Product.class)
                    .setFetchMode("productSupermarkets", FetchMode.JOIN)
                    .add(Restrictions.eq("productId", product.getProductId()))
                    .uniqueResult();
            Set<ProductSupermarket> productSupermarkets = product.getProductSupermarkets();
            transaction.commit();
            logger.info("ProductSupermarkets Retrieved");
            return productSupermarkets;
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
    }
    
    @Override
    public synchronized Set<ProductSupermarket> FindAllProductSupermarkets(Supermarket supermarket)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Get ProductSupermarkets");
            transaction = session.beginTransaction();
            supermarket = (Supermarket) session.createCriteria(Supermarket.class)
                        .setFetchMode("productSupermarkets", FetchMode.JOIN)
                        .add(Restrictions.eq("supermarketId", supermarket.getSupermarketId()))
                        .uniqueResult();
            Set<ProductSupermarket> productSupermarkets = supermarket.getProductSupermarkets();
            transaction.commit();
            logger.info("ProductSupermarkets Retrieved");
            return productSupermarkets;
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
    }
    
    @Override
    public synchronized boolean DeleteProduct(Product product)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Begin Delete Product");
            System.out.println("Begin Delete Product");
            transaction = session.beginTransaction();
            Product getProduct = (Product) session.load(Product.class, product.getProductId());
            session.delete(getProduct);
            transaction.commit();
            logger.info("Delete Product Done");
            System.out.println("Delete Product Done");
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
        return true;
    }
    
    @Override
    public synchronized boolean DeleteSupermarket(Supermarket supermarket)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Begin Delete Supermarket");
            System.out.println("Begin Delete Supermarket");
            transaction = session.beginTransaction();
            Supermarket getSupermarket = (Supermarket) session.load(Supermarket.class, supermarket.getSupermarketId());
            session.delete(getSupermarket);
            transaction.commit();
            logger.info("Delete Supermarket Done");
            System.out.println("Delete Supermarket Done");
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
        return true;
    }
    
    @Override
    public synchronized boolean DeleteProductFromSupermarket(ProductSupermarket productSupermarket)
    {
        Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Begin Delete Product from Supermarket");
            System.out.println("Begin Delete Product from Supermarket");
            transaction = session.beginTransaction();
            ProductSupermarket getProductSupermarket = (ProductSupermarket) session.load(ProductSupermarket.class, productSupermarket.getPk());
            session.delete(getProductSupermarket);
            transaction.commit();
            logger.info("Delete Supermarket Done");
            System.out.println("Delete Supermarket Done");
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
        return true;
    }
}